﻿using System;
using System.Linq;
using System.Net;
using SpringGraph.ForceLayout;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using SpringGraph.Extensions;

namespace SpringGraph
{
    public class GraphDataProvider : IDataProvider
    {
        private Dictionary<string, GraphNode> nodeStore = new Dictionary<string, GraphNode>();
        private List<GraphNode> nodes;
        private List<GraphEdge> edges;
	    private SpringGraph host;	
	    private bool _layoutChanged = false;
	    
        public int Distance;
        public Rect Boundary
        {
            get
            {
                var minX = nodes.Select(node => node.X).Min();
                var maxX = nodes.Select(node => node.X).Max();
                var minY = nodes.Select(node => node.Y).Min();
                var maxY = nodes.Select(node => node.Y).Max();

                return new Rect(new Point(minX, minY), new Point(maxX, maxY));
            }
        }
        public List<GraphEdge> Edges
        {
            get
            {
                return edges;
            }
        }

        public List<GraphNode> Nodes
        {
            get
            {
                return nodes;
            }
        }

        public bool LayoutChanged
        {
            get
            {
                return _layoutChanged;
            }
            set
            {
                _layoutChanged = value;
            }
        }

        public GraphDataProvider(SpringGraph host)
        {
            this.host = host;
        }

        private GraphNode MakeGraphNode(Item item)
        {
            GraphNode result;
		    if(nodeStore.ContainsKey(item.Id)) {
			    result = nodeStore[item.Id];
			    if(result.View.Parent == null)
				    host.Children.Add(result.View);	
		    } else {
			    result = new GraphNode(host.NewComponent(item), this, item);
			    nodeStore[item.Id] = result;
		    }
		    return result;
	    }


        #region IDataProvider Members

        public void ForAllNodes(IForEachNode fen)
        {
            foreach (var node in nodes) {
			    fen.ForEachNode(node);
		    }
        }

        public void ForAllEdges(IForEachEdge fee)
        {
            foreach (var edge in edges)
            {
                fee.ForEachEdge(edge);
            }
        }

        public void ForAllNodePairs(IForEachNodePair fenp)
        {
            foreach (var nodeI in nodes)
            {
                foreach (var nodeJ in nodes)
                {
                    if (nodeI != nodeJ)
                        fenp.ForEachNodePair(nodeI, nodeJ);
                }
            }
        }

        public Graph Graph
        {
            set
            {
                var g = value;
                var newItems = g.Nodes;
		        var newEdges = g.Edges;
        		
		        // re-create the list of nodes
		        var oldNodes = nodes;
		        nodes = new List<GraphNode>();
		        foreach (var item in newItems) {
			        nodes.Add(MakeGraphNode(item));
		        }
		        if(oldNodes != null) {
			        foreach (var oldNode in oldNodes) {
				        if(!g.HasNode(oldNode.Item.Id)) {
					        // this node is not in the currently displayed set
					        if(oldNode.View.Parent != null)
						        host.Children.Remove(oldNode.View);
						        nodeStore.Remove(oldNode.Item.Id);
						        // !!@ how does it get re-added
				        }
			        }
		        }

		        // re-create the list of edges
		        edges = new List<GraphEdge>();
		        foreach (var edge in newEdges) {
			        edges.Add(new GraphEdge(nodeStore[edge.Key.Id], nodeStore[edge.Value.Id], Distance));
		        }
            }
        }

        #endregion

        public bool HasNodes
        {
            get
            {
                return nodes != null && nodes.Count > 0;
            }
        }

        public GraphNode FindNode(string id)
        {
            return nodes.Where(node => node.Item.Id == id).FirstOrDefault();
        }

        public double RepulsionFactor
        {
            get
            {
                return host.RepulsionFactor;
            }
        }

        public double DefaultRepulsion
        {
            get
            {
                return host.DefaultRepulsion;
            }
        }
    }
}
